﻿#include "pch.h"
#if _QG_USE_GL
#include "qgdrv_gl_rdh.h"
#include "qgdrv_gl_cnv.h"

//
void GLRdh::_CommitShader() throw()
{
	GLShader* shd = _pend.shd_ptr;

	if (shd == NULL)
	{
		if (_sion.shd_program != 0)
		{
			glUseProgram(0);

			_sion.shd_program = 0;
		}
	}
	else
	{
		if (!shd->_linked)
		{
			// 링크 안됨
			kobj_tracet(KLV_WARNING, KERR_NOREF, "no linked shader specified. will be linking instead.");
			kobj_tracef(KLV_WARNING, "target shader is '%s'", shd->_key.c_str());

			shd->Link();
		}

		GLuint program = (GLuint)shd->GetDescription();

		if (_sion.shd_program != program)
		{
			glUseProgram(program);

			_sion.shd_program = program;
		}
	}
}

//
void GLRdh::_ProcessShaderMap() throw()
{
	if (_pend.shd_ptr != NULL)
		_pend.shd_ptr->_ProcessMap();
}

//
qgResObj* GLRdh::_RoamInitShader(const char* name)
{
	GLShader* base = kobj_create(GLShader);

	if (name)
		base->_key = name;
	else
	{
		char sz[32];
		k_snprintf(sz, 32 - 1, "%Lu", k_nth());

		base->_key = "gl_shader_";
		base->_key += sz;
	}

	return base;
}


//////////////////////////////////////////////////////////////////////////
// 세이더

//
void GLShader::TypeInitialize(kType type) throw()
{
	_AutoInitFunc();
}

//
GLShader::GLShader() throw()
{
	_node_desc = (kuintptr)glCreateProgram();
}

//
GLShader::~GLShader() throw()
{
	_RemoveHandle(_rvp, 0);
	_RemoveHandle(_rfp, 0);

	glDeleteProgram((GLuint)_node_desc);
}

//
void GLShader::_PrintError(GLuint handle, kint errcode, const char* target, bool shdinfo, bool prginfo) throw()
{
	//kobj_tracef(KLV_ERROR, "%s in '%s' with program '%s'", k_errmsg(errcode), target, _key.Data());
	kobj_tracef(KLV_ERROR, "'%s', %s of '%s'", _key.c_str(), k_errmsg(errcode), target);

	if (shdinfo)
	{
		GLint maxlen = 0;
		glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &maxlen);

		if (maxlen > 0)
		{
			GLint len;
			GLchar* psz = k_local_new(maxlen, GLchar);
			glGetShaderInfoLog(handle, maxlen, &len, psz);

			kobj_tracef(KLV_ERROR, "shader %s", psz);
		}
	}

	if (prginfo)
	{
		GLint maxlen = 0;
		glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &maxlen);

		if (maxlen > 0)
		{
			GLint len;
			GLchar* psz = k_local_new(maxlen, GLchar);
			glGetProgramInfoLog(handle, maxlen, &len, psz);

			kobj_tracef(KLV_ERROR, "program %s", psz);
		}
	}
}

//
GLShader::RefHandle* GLShader::_CompileProgram(qgShaderType type, const char* src, GLint len) throw()
{
	// http://msdn.microsoft.com/ko-kr/library/windows/apps/dn166905.aspx

	GLenum gltype = GLConvert::ShaderType(type);
	GLuint sh = glCreateShader(gltype);

	if (sh == 0)
		return NULL;

	//
	glShaderSource(sh, 1, &src, &len);
	glCompileShader(sh);

	GLint status = 0;
	glGetShaderiv(sh, GL_COMPILE_STATUS, &status);

	if (!status)
	{
		_PrintError(sh, KERR_PARSE, qgResSupp::ShaderTypeToString(type), true, false);
		return NULL;
	}

	return new RefHandle(sh);
}

//
bool GLShader::_BindData(qgShaderType type, kconstpointer data, kint size, int flags) throw()
{
	// 매크로 패스
	// 인클루드 패스
	// 플래그 패스
	// 킵 없음

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_VS:
			_RemoveHandle(_rvp, (GLuint)_node_desc);
			if ((_rvp = _CompileProgram(QGSHADER_VS, (const char*)data, (GLint)size)) == NULL)
			{
				K_UFLAG(&_insts, QGSHDI_VS, false);
				return false;
			}
			else
			{
				glAttachShader((GLuint)_node_desc, _rvp->handle);
				K_UFLAG(&_insts, QGSHDI_VS, true);
			}
			break;

		case QGSHADER_PS:
			_RemoveHandle(_rfp, (GLuint)_node_desc);
			if ((_rfp = _CompileProgram(QGSHADER_PS, (const char*)data, (GLint)size)) == NULL)
			{
				K_UFLAG(&_insts, QGSHDI_PS, false);
				return false;
			}
			else
			{
				glAttachShader((GLuint)_node_desc, _rfp->handle);
				K_UFLAG(&_insts, QGSHDI_PS, true);
			}
			break;

		default:
			return false;
	}

	_linked = false;

	return true;
}

//
bool GLShader::BindFile(qgShaderType type, const char* filename, kint flags)
{
	kint readsize;
	kpointer readdata = qgRdh::Instance->GetStub()->FileRead(filename, &readsize);

	k_return_value_if_fail(readdata, false);

	// 후... 이거 널값 안넣어 주면 안됨.
	// 다행히도 FileRead에서 4바이트 더 해줌. 원래 UCS-4용으로 만든건데 다행
	kbyte* ptr = (kbyte*)readdata;
	ptr[readsize] = '\0';

	//
	bool ret = _BindData(type, readdata, readsize, flags);

	k_delete(readdata);

	return ret;
}

//
bool GLShader::BindData(qgShaderType type, kconstpointer data, kint size, kint flags)
{
	k_return_value_if_fail(data && size > 0, false);

	return _BindData(type, data, size, flags);
}

//
bool GLShader::BindShader(qgShaderType type, qgShader* shdptr)
{
	k_return_value_if_fail(shdptr, false);

	// 찾는게 있으면 세이더를 참조해둔다
	GLShader* shd = (GLShader*)shdptr;
	bool ret;

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_VS:
			_RemoveHandle(_rvp, (GLuint)_node_desc);

			if (!shd->_rvp)
			{
				ret = false;
				_rvp = NULL;
			}
			else
			{
				ret = true;
				_rvp = shd->_rvp;

				k_atominc(&_rvp->ref);

				glAttachShader((GLuint)_node_desc, _rvp->handle);
			}

			K_UFLAG(&_insts, QGSHDI_VS, ret);
			break;

		case QGSHADER_PS:
			_RemoveHandle(_rfp, (GLuint)_node_desc);

			if (!shd->_rfp)
			{
				ret = false;
				_rfp = NULL;
			}
			else
			{
				ret = true;
				_rfp = shd->_rfp;

				k_atominc(&_rfp->ref);

				glAttachShader((GLuint)_node_desc, _rfp->handle);
			}

			K_UFLAG(&_insts, QGSHDI_PS, ret);
			break;

		default:
			ret = false;
			break;
	}

	if (!ret)
		return false;
	else
	{
		// 링크 다시해!
		_linked = false;
		return true;
	}
}

//
bool GLShader::BindShader(qgShaderType type, const char* name)
{
	k_return_value_if_fail(name, false);

	// 찾는다
	KeyType key(name);

	GLShader* shd = (GLShader*)((GLRdh*)qgRdh::Instance)->_RoamFind(QGROAM_MTL_2, &key);

	return BindShader(type, shd);
}

//
bool GLShader::Link()
{
	k_return_value_if_ok(_linked, true);

	if (!_rfp && !_rvp)
	{
		// 참조할 세이더가 모두 없다
		kobj_tracep(KLV_ERROR, KERR_ARGUMENT);
		return false;
	}

	//
	GLuint sh = (GLuint)_node_desc;

	glLinkProgram(sh);

	GLint status = 0;
	glGetProgramiv(sh, GL_LINK_STATUS, &status);

	if (!status)
	{
		_PrintError(sh, KERR_PARSE, "Link", false, true);
		return false;
	}

	// 여기서 분석한다
	GLint cnt = 0, maxlen = 0;
	char sz[64];

	// 유니폼 -> 전역 변수
	glGetProgramiv(sh, GL_ACTIVE_UNIFORMS, &cnt);

	if (cnt > 0)
	{
		glGetProgramiv(sh, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxlen);

		if (maxlen == 0 || maxlen > 31)
		{
			// 굳이 오류는 아님.
			kobj_tracet(KLV_WARNING, KERR_PROPERTY, "invalid maximum length of uniforms.");
		}

		//
		_vars.resize(cnt);

		for (GLint i = 0; i < cnt; i++)
		{
			GLint glsize;
			GLenum gltype;
			glGetActiveUniform(sh, i, 32 - 1, NULL, &glsize, &gltype, sz);

			qgShaderData datatype = _GLTypeToShaderData(gltype);
			if (datatype == QGSHDD_UNKNOWN)
			{
				kobj_tracet(KLV_WARNING, KERR_SYNTAX, "no support shader uniform data type.");
				kobj_tracef(KLV_WARNING, "type id '%X' of shader '%s'", gltype, _key.c_str());
			}

			qbShaderVar& v = _vars[i];
			k_strncpy(v.name, sz, 32 - 1);
			v.hash = k_strihash(v.name);
			v.offset = glGetUniformLocation(sh, sz);
			v.size = (kint)glsize;
			v.data = datatype;
			_AutoMatchFunc(v);
			v.ptr = NULL; // baad 데이터가 있으면 좀 그러니깐 초기화
		}
	}

	// 속성 -> 인수
	glGetProgramiv(sh, GL_ACTIVE_ATTRIBUTES, &cnt);

	if (cnt > 0)
	{
		glGetProgramiv(sh, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxlen);

		if (maxlen == 0)
		{
			// 굳이 오류는 아님.
			//kobj_tracet(KLV_WARNING, KERR_PROPERTY, "invalid maximum length of attributes.");

			// 이 경우는 드라이버가 gl_로 시작하는 애들을 attribute로 처리했을 경우에다,
			// 사용자가 지정한 attribute가 하나도 없을 때를 의미함.
			//
			// 고정 기능과 호환되는 기능을 사용하기 때문에 attribute로 묶을 필요는 없다
			// 어짜피 glGetAttribLocation() 함수가 -1을 반환하기 때문에 쓸 수도 없다
			//
			// 근데 고정 기능 안쓰는데???
		}

		//
		_atrs.resize(cnt);
		_amask = 0;

		for (kint i = 0; i < QGDLOU_MAX_VALUE; i++)
		{
			_acnt[i] = 0;
			_alnk[i] = NULL;
		}

		for (GLint i = 0; i < cnt; i++)
		{
			GLint glsize;
			GLenum gltype;
			glGetActiveAttrib(sh, i, 32 - 1, NULL, &glsize, &gltype, sz);

			qgShaderData datatype = _GLTypeToShaderData(gltype);
			if (datatype == QGSHDD_UNKNOWN)
			{
				kobj_tracet(KLV_WARNING, KERR_SYNTAX, "no support shader attribute data type.");
				kobj_tracef(KLV_WARNING, "type id '%X' of shader '%s'", gltype, _key.c_str());
			}

			AttribInfo& a = _atrs[i];
			k_strncpy(a.name, sz, 32 - 1);
			a.hash = k_strihash(a.name);
			a.data = datatype;
			a.size = (kuint)glsize;
			a.attrib = glGetAttribLocation(sh, sz);

			if (a.attrib < 0)
			{
				a.usage = (qgDloUsage)0xFF;
				a.index = -1;
				a.next = NULL;
			}
			else
			{
				a.usage = _GLAttribToLayoutUsage(sz);

				if (a.usage >= QGDLOU_MAX_VALUE)
				{
					a.index = 0;
					a.next = NULL;
				}
				else
				{
					a.index = _acnt[a.usage];
					_acnt[a.usage]++;

					// 인덱스 검사를 위해 저장한다. 단, 저장 순서는 거꾸로라는거 주의
					a.next = _alnk[a.usage];
					_alnk[a.usage] = &a;
				}

				// 마스킹
				//_amask |= 1 << a.usage;
			}
		}
	}

	//
	_linked = true;

	return true;
}

//
GLShader::AttribInfo* GLShader::FindAttribInfo(qgDloUsage usage, kint index) throw()
{
#if 0
	// 이 루틴... 리얼타임으로 불릴텐데 느릴 것 같다.
	kForEach(AttribVar& a, _atrs)
	{
		if (usage == a.usage && index == a.index)
			return &a;
	}

	return NULL;
#else
	AttribInfo* v = _alnk[usage];
	for (kint i = 0; i < index; i++)
	{
		if (!v)
			break;
		v = v->next;
	}
	return v;
#endif
}

//
void GLShader::_ProcessMap() throw()
{
	kForEach(qbShaderVar& v, _vars)
	{
		// OPENGL은 data에 offset을 넣는다. 안넣어도 되고
		if (v.role == QGSHDR_AUTO)
			((qbShaderAutoInfo::Function)v.intr)(qgRdh::Instance, K_CAST_INT_TO_PTR(v.offset), v);
	}

	// 사용자 지정
	if (_cbintr.func != NULL)
		((IntrFunction)_cbintr.func)(_cbintr.data, _vars.size(), &(*_vars.begin()), this);
}

//
void GLShader::_RemoveHandle(RefHandle* rh, GLuint program) throw()
{
	if (rh)
	{
		if (program > 0)
			glDetachShader(program, rh->handle);

		if (k_atomdec(&rh->ref) == 0)
		{
			glDeleteShader(rh->handle);

			delete rh;
		}
	}
}

//
qgShaderData GLShader::_GLTypeToShaderData(GLenum type) throw()
{
	switch (K_CAST_ENUM(type))
	{
		case GL_FLOAT:
			return QGSHDD_FLOAT1;

		case GL_FLOAT_VEC2:
			return QGSHDD_FLOAT2;

		case GL_FLOAT_VEC3:
			return QGSHDD_FLOAT3;

		case GL_FLOAT_VEC4:
			return QGSHDD_FLOAT4;

		case GL_FLOAT_MAT4:
			return QGSHDD_FLOAT16;

		case GL_SAMPLER_2D:
			return QGSHDD_INT1;

		case GL_SAMPLER_CUBE:
			return QGSHDD_INT1;

		case GL_INT:
			return QGSHDD_INT1;

		case GL_INT_VEC2:
			return QGSHDD_INT2;

		case GL_INT_VEC3:
			return QGSHDD_INT3;

		case GL_INT_VEC4:
			return QGSHDD_INT4;

		case GL_BOOL:
			return QGSHDD_BYTE1;

		case GL_BOOL_VEC2:
			return QGSHDD_BYTE2;

		case GL_BOOL_VEC3:
			return QGSHDD_BYTE3;

		case GL_BOOL_VEC4:
			return QGSHDD_BYTE4;

#ifdef GL_SAMPLER_1D
		case GL_SAMPLER_1D:
			return QGSHDD_INT1;
#endif

#ifdef GL_SAMPLER_3D
		case GL_SAMPLER_3D:
			return QGSHDD_INT1;
#endif

#ifdef GL_SAMPLER_1D_SHADOW
		case GL_SAMPLER_1D_SHADOW:
			return QGSHDD_INT1;
#endif

#ifdef GL_SAMPLER_2D_SHADOW
		case GL_SAMPLER_2D_SHADOW:
			return QGSHDD_INT1;
#endif
	}

	return QGSHDD_UNKNOWN;
}

//
qgDloUsage GLShader::_GLAttribToLayoutUsage(const char* name) throw()
{
	// 각 속성 이름에 "+숫자" 일 경우가 있기 때문에 길이 비교를 한다
	// 예를 들면 색깔은 acolor 또는 acolor0, 색깔2(스페큘러)는 acolor1 이런 식

	if (k_strnicmp(name, "aposition", 9) == 0)
		return QGDLOU_POSITION;

	if (k_strnicmp(name, "acolor", 6) == 0)
		return QGDLOU_COLOR;

	if (k_strnicmp(name, "aweight", 7) == 0)
		return QGDLOU_WEIGHT;

	if (k_strnicmp(name, "aindex", 6) == 0)
		return QGDLOU_INDEX;

	if (k_strnicmp(name, "anormal", 7) == 0)
		return QGDLOU_NORMAL;

	if (k_strnicmp(name, "atexcoord", 9) == 0 ||
		k_strnicmp(name, "acoord", 6) == 0)
		return QGDLOU_TEXTURE;

	if (k_strnicmp(name, "atangent", 8) == 0)
		return QGDLOU_TANGENT;

	if (k_strnicmp(name, "abinormal", 9) == 0)
		return QGDLOU_BINORMAL;

	return (qgDloUsage)0xFF;
}

// 자동 변수
namespace GLShaderWorker
{
	void OrthoProj(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 1);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().t_otm.ToPointer());
	}

	void World(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 1);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().t_tm[QGTM_WORLD].ToPointer());
	}

	void View(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 1);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().t_tm[QGTM_VIEW].ToPointer());
	}

	void Proj(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 1);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().t_tm[QGTM_PROJ].ToPointer());
	}

	void ViewProj(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 1);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().t_vipr.ToPointer());
	}

	void InvView(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 1);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().t_invv.ToPointer());
	}

	void WorldViewProj(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 1);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, (rdh->GetPrsn().t_tm[QGTM_WORLD] * rdh->GetPrsn().t_vipr).ToPointer());
	}

	void Tex0(GLRdh* dev, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
		//glUniform1i(K_CAST_PTR_TO_INT(data), 0);
	}

	void Tex1(GLRdh* dev, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
		//glUniform1i(K_CAST_PTR_TO_INT(data), 1);
	}

	void Tex2(GLRdh* dev, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
		//glUniform1i(K_CAST_PTR_TO_INT(data), 2);
	}

	void Tex3(GLRdh* dev, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
		//glUniform1i(K_CAST_PTR_TO_INT(data), 3);
	}

	void Tex4(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
	}

	void Tex5(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
	}

	void Tex6(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
	}

	void Tex7(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_INT1);
	}

	void PropVec0(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		glUniform4fv(K_CAST_PTR_TO_INT(data), 1, rdh->GetPrsn().ps_v[0].ToPointer());
	}

	void PropVec1(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		glUniform4fv(K_CAST_PTR_TO_INT(data), 1, rdh->GetPrsn().ps_v[1].ToPointer());
	}

	void PropVec2(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		glUniform4fv(K_CAST_PTR_TO_INT(data), 1, rdh->GetPrsn().ps_v[2].ToPointer());
	}

	void PropVec3(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		glUniform4fv(K_CAST_PTR_TO_INT(data), 1, rdh->GetPrsn().ps_v[3].ToPointer());
	}

	void PropMat0(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().ps_m[0].ToPointer());
	}

	void PropMat1(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().ps_m[1].ToPointer());
	}

	void PropMat2(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().ps_m[2].ToPointer());
	}

	void PropMat3(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), 1, false, rdh->GetPrsn().ps_m[3].ToPointer());
	}

	void MatPalette(GLRdh* rdh, kpointer data, const qbShaderVar& v)
	{
		auto& prsn = rdh->GetPrsn();
		glUniformMatrix4fv(K_CAST_PTR_TO_INT(data), prsn.pw_cnt, false, (GLfloat*)&prsn.pw_pcb[0]);
	}

#define SV(m,t)	{0, "s" K_STRING(m), t, (qbShaderAutoInfo::Function)m}
	qbShaderAutoInfo Autos[QGSHDA_MAX_VALUE] =
	{
		SV(OrthoProj, QGSHDA_ORTHO_PROJ),
		SV(World, QGSHDA_WORLD),
		SV(View, QGSHDA_VIEW),
		SV(Proj, QGSHDA_PROJ),
		SV(ViewProj, QGSHDA_VIEW_PROJ),
		SV(InvView, QGSHDA_INV_VIEW),
		SV(WorldViewProj, QGSHDA_WORLD_VIEW_PROJ),
		SV(Tex0, QGSHDA_TEX0),
		SV(Tex1, QGSHDA_TEX1),
		SV(Tex2, QGSHDA_TEX2),
		SV(Tex3, QGSHDA_TEX3),
		SV(Tex4, QGSHDA_TEX4),
		SV(Tex5, QGSHDA_TEX5),
		SV(Tex6, QGSHDA_TEX6),
		SV(Tex7, QGSHDA_TEX7),
		SV(PropVec0, QGSHDA_PROP_VEC0),
		SV(PropVec1, QGSHDA_PROP_VEC1),
		SV(PropVec2, QGSHDA_PROP_VEC2),
		SV(PropVec3, QGSHDA_PROP_VEC3),
		SV(PropMat0, QGSHDA_PROP_MAT0),
		SV(PropMat1, QGSHDA_PROP_MAT1),
		SV(PropMat2, QGSHDA_PROP_MAT2),
		SV(PropMat3, QGSHDA_PROP_MAT3),
		SV(MatPalette, QGSHDA_MAT_PALETTE),
	};
#undef SV
}

//
void GLShader::_AutoInitFunc()
{
	for (ksize_t i = 0; i < K_COUNTOF(GLShaderWorker::Autos); i++)
		GLShaderWorker::Autos[i].hash = k_strihash(GLShaderWorker::Autos[i].name);
}

//
kpointer GLShader::_AutoMatchFunc(qbShaderVar& v)
{
	for (ksize_t i = 0; i < K_COUNTOF(GLShaderWorker::Autos); i++)
	{
		if (v.hash == GLShaderWorker::Autos[i].hash &&
			k_strieqv(v.name, GLShaderWorker::Autos[i].name))
		{
			// 찾음
			v.role = QGSHDR_AUTO;
			v.intr = (kpointer)GLShaderWorker::Autos[i].func;

			return NULL;
		}
	}

	// 없음
	v.role = QGSHDR_MANUAL;
	v.intr = NULL;

	return NULL;
}

#endif	// _QG_USE_GL
